﻿namespace MathVisualizer

module Evaluator =
    open System.Drawing 
    open MathVisualizer.Utils 
    open MathVisualizer.DrawLibrary 

    let rec eval1 (g:Graphics, node:Expr, level:int) =
        let mutable container = node.Container
        match node.Expr with
        |BigSum(p1, p2, arg) | Integral(p1, p2, arg) | Union(p1, p2, arg) | Intersection(p1, p2, arg) | Productor(p1, p2, arg) ->
            let height = 56.f
            let name, width = match node.Expr with
                                | BigSum(_, _, _) -> "bigsum", getBigSumBoxWidth height
                                | Integral(_, _, _) -> "int", getIntBoxWidth height
                                | Union(_, _, _) -> "union", getUnionBoxWidth height
                                | Intersection(_, _, _) -> "intersection", getUnionBoxWidth height
                                | Productor(_, _, _) -> "productor", getProductorBoxWidth height
                                | _ -> "", 0.f

            let mutable sybox = new RectangleF(0.f, 0.f, width, height)
            container <- sybox
            eval1 (g, p1, level+1)
            eval1(g, p2, level+1)
            eval1(g, arg, level)
            node.ExprString <- name + "(" + p1.ExprString + "," + p2.ExprString + "," + arg.ExprString + ")"
            printf "eval1 bigsum exprstring: %s\n" node.ExprString 
            let mutable p1box = p1.Container
            let mutable p2box = p2.Container
            let mutable argbox = arg.Container

            let maxwidth = System.Math.Max(sybox.Width, System.Math.Max(p1box.Width, p2box.Width))
            let sumboxheight = p2box.Height + sybox.Height + p1box.Height 
            let sumboxYOffset = if (argbox.Height > sumboxheight) then ((argbox.Height - sumboxheight) / 2.f)  else 0.f

            //quando argbox.height / sumboxheight viene centrato tutto il sumbox(p1 + p2+ sybox) rispetto ad argbox. 
            //Dovrebbe centrare solo sybox. Bug ricreabile creando una bigsum con p2 alto (1/2/3) e crendo un argbox via via + alto
            
            p2box.Location <- PointF((maxwidth - p2box.Width) / 2.f, sumboxYOffset)
            sybox.Location <- PointF((maxwidth - sybox.Width) / 2.f, p2box.Y + p2box.Height)
            p1box.Location <- PointF((maxwidth - p1box.Width) / 2.f, sybox.Y + sybox.Height)
            
            argbox.X <- maxwidth + 3.f
            argbox.Y <- if (argbox.Height > sumboxheight) then 0.f else sybox.Y + (centerRelY sybox argbox)

            container.Size <- SizeF(argbox.X + argbox.Width, System.Math.Max(argbox.Height + argbox.Y, container.Height + p1box.Height + p2box.Height))
            node.MiddleY <- sybox.Y + sybox.Height / 2.f

            node.Box <- sybox
            p1.Container <- p1box
            p2.Container <- p2box
            arg.Container <- argbox
        
            ()


        |Sum(e1, e2) | Sub(e1, e2) | Equals(e1, e2) | Concat(e1, e2) | LeftArrow(e1, e2) | RightArrow(e1, e2) | Major(e1, e2) | Minor(e1, e2) | Geq(e1, e2)
        |Leq(e1, e2) | Approx(e1, e2) | Mul(e1, e2) ->
            eval1(g, e1, level)
            eval1(g, e2, level)
            let symbol, longstr, shortstr = match node.Expr with
                                                |Sum(e1, e2) -> "+", "sum", "e1 + e2"
                                                |Sub(e1, e2) -> "-", "sub", "e1 - e2"
                                                |Equals(e1, e2) -> "=", "equals", "e1 = e2"
                                                |Concat(e1, e2) -> "", "concat", "e1 e2"
                                                |LeftArrow(e1, e2) -> "", "leftarrow", "e1 \from e2"
                                                |RightArrow(e1, e2) -> "", "rightarrow", "e1 \to e2"
                                                |Major(e1, e2) -> ">", "major", "e1 > e2"
                                                |Minor(e1, e2) -> "<", "minor", "e1 < e2"
                                                |Geq(e1, e2) -> "\u2265", "geq", "e1 \geq e2"
                                                |Leq(e1, e2) -> "\u2264", "leq", "e1 \leq e2"
                                                |Approx(e1, e2) -> "\u2248", "approx", "e1 \appr e2"
                                                |Mul(e1, e2) -> "*", "mul", "e1 * e2"
                                                | _ -> "", "", ""

            
            let mutable sybox = match node.Expr with
                                    | LeftArrow(e1, e2) | RightArrow(e1, e2) ->
                                                                            //l'altezza delle freccia e' altezza media di e1 ed e2
                                                                            let syboxheight = (e1.Container.Height + e2.Container.Height) / 4.f
                                                                            RectangleF(0.f, 0.f, getArrowBoxWidth syboxheight, syboxheight)
                                    | _ -> measureString(g, symbol, font(level, false))
            container <- sybox

            node.ExprString <- match node.IsContract with
                                |true -> shortstr.Replace("e1", e1.ExprString).Replace("e2", e2.ExprString)
                                | _ -> longstr + "(" + e1.ExprString + "," + e2.ExprString + ")"

            let mutable e1box = e1.Container
            let mutable e2box = e2.Container
        
            sybox.X <- e1box.Width
            e2box.X <- sybox.X + sybox.Width
            container.Width <- sybox.Width + e1box.Width + e2box.Width
        
            let localmiddleY = System.Math.Max(e1.MiddleY, e2.MiddleY)
            
            printf "SUM localmiddleY: %f; e1box.middleY: %f; e2box.middleY: %f; sybox.Height: %f;\n" localmiddleY e1.MiddleY e2.MiddleY sybox.Height 

            e1box.Y <- localmiddleY - e1.MiddleY
            e2box.Y <- localmiddleY - e2.MiddleY
            sybox.Y <- localmiddleY - sybox.Height / 2.f
        
            container.Height <- System.Math.Max(e1box.Y + e1box.Height, e2box.Y + e2box.Height)
        
            node.MiddleY <- sybox.Y + sybox.Height / 2.f
        
        
            //debug("Sum: sybox.width: " + sybox.Width.ToString() + "; e1box.width: " + e1box.Width.ToString() + "; e2box.width: " + e2box.Width.ToString() + " e2box.location: " + e2box.Location.ToString())
        
            node.Box <- sybox
        
            e1.Container <- e1box
            e2.Container <- e2box

            ()

        |Ratio(e1, e2) ->
            eval1(g, e1, level)
            eval1(g, e2, level)
            debug "RATIO"
            node.ExprString <- match node.IsContract with
                                |true -> e1.ExprString + "/" + e2.ExprString
                                | _ ->  "ratio(" + e1.ExprString + "," + e2.ExprString + ")"
            let mutable e1box = e1.Container
            let mutable e2box = e2.Container
        
            container.Height <- e1box.Height + e2box.Height + 5.f
            container.Width <- System.Math.Max(e1box.Width, e2box.Width)
        
            e2box.Y <- e1box.Height + 5.f
            e2box.X <- container.Width / 2.f - e2box.Width / 2.f
        
            e1box.Y <- 0.f
            e1box.X <- container.Width / 2.f - e1box.Width / 2.f
        
            node.MiddleY <- e1box.Height + 3.f
        
            e1.Container <- e1box
            e2.Container <- e2box
        
            ()
        
        |Exponent(e1, e2) | Subscript(e1, e2) ->
            eval1(g, e1, level)
            eval1(g, e2, level+1)
        
            
            let mutable e1box = e1.Container
            let mutable e2box = e2.Container
            
            //e1box.Y <-e2box.Height / 2.f
            let mutable name, contractsymbol = "", ""

            match node.Expr with
               |Exponent(_, _) -> 
                    //debug("i'm exponent")
                    e1box.Y <- e2box.Y + System.Math.Max(10.f, e2box.Height) - 10.f//e2box.Height / 2.f
                    container.Height <- System.Math.Max(e1box.Height + e1box.Y, e2box.Y + e2box.Height)
                    name <- "exponent" 
                    contractsymbol <- "^"
               |Subscript(_, _) -> 
                    //debug("i'm subscript; e2box.Y: " + e2box.Y.ToString() + "; container.Height: " + container.Height.ToString())
                    e2box.Y <- e1box.Y + System.Math.Max(e1box.Height, 10.f) - 10.f
                    container.Height <- System.Math.Max(e1box.Height + e1box.Y, e2box.Y + e2box.Height)
                    //debug("i'm subscript 2; e2box.Y: " + e2box.Y.ToString() + "; container.Height: " + container.Height.ToString())
                    name <- "subscript"
                    contractsymbol <- "_"
               | _ -> ()
            
            node.ExprString <- match node.IsContract with
                                |true -> e1.ExprString + contractsymbol + e1.ExprString
                                | _ -> name + "(" + e1.ExprString + "," + e2.ExprString + ")"

            e2box.X <- match node.Expr, e1.Expr with
                        |Exponent(e1, e2), Subscript(c1, c2) | Subscript(e1, e2), Exponent(c1, c2)->
                            debug("MY CHILD IS SUBSCRIPT; c2.Container.X: " + c2.Container.X.ToString())
                            container.Width <- c1.Container.Width + System.Math.Max(c2.Container.Width, e2.Container.Width)
                            c2.Container.X 
                        | _ -> 
                            container.Width <- e1box.Width + e2box.Width
                            e1box.Width 
            
            //e2box.X <- e1box.Width
            //node.MiddleY <- e1box.Y + e1box.Height / 2.f
            (*node.MiddleY <- match e1.Expr with
                                |Subscript(c1, c2) -> printf "c1 is Subscript: c1: %s; e2:%s; c1.MiddleY: %f; e1box.Y: %f\n" c1.ExprString e2.ExprString c1.MiddleY e1box.Y; e1box.Y + c1.MiddleY
                                |Exponent(c1, c2) -> printf "c1 is Exponent: c1: %s; e2:%s; c1.MiddleY: %f; e1box.Y: %f\n" c1.ExprString e2.ExprString c1.MiddleY e1box.Y; e1box.Y + c1.MiddleY
                                | _ -> printf "c1 is other; e1box.Y: %f; e1box.Height: %f; middleY: %f" e1box.Y e2box.Height (e1box.Y + e1box.Height / 2.f); e1box.Y + e1box.Height / 2.f
            *)
            node.MiddleY <- e1box.Y + e1.MiddleY
                
            //container.Height <- e1box.Height + e1box.Y
            e1.Container <- e1box
            e2.Container <- e2box

        |Round(e) | Square(e) | Brace(e) | Group(e)->
            eval1(g, e, level)
            node.ExprString <- match node.Expr with
                               | Round(e) -> "(" + e.ExprString + ")"
                               | Square(e) -> "[" + e.ExprString + "]"
                               | Brace(e) -> "\{" + e.ExprString + "\}"
                               | Group(e) -> "{" + e.ExprString + "}"
                               | _ -> ""

            let mutable ebox = e.Container

            let height = System.Math.Max(1.f, ebox.Height)

            let roundboxwidth = match node.Expr with
                                |Round(e) -> getRoundBoxWidth height
                                |Square(e) -> getSquareBoxWidth height
                                |Brace(e) -> getBraceBoxWidth height
                                | _ -> 0.f

            let roundbox = new RectangleF(0.f, 0.f, roundboxwidth, height)//measureString(g, "(", new Font(stdfont,  height))

            //let gopen = openRound ebox
        

            ebox.X <- roundbox.Width


            ebox.Y <- centerRelY roundbox ebox
        
            ebox.Height <- height // it's used for font size in paintTree => should be greater than 0

            container.Width <- roundbox.Width*2.f + ebox.Width
            container.Height <- roundbox.Height + 1.f

            node.MiddleY <- ebox.Y + e.MiddleY
            node.Box <- roundbox

            e.Container <- ebox

       
            ()

        |Matrix(rows, cols, elements) ->
            let getindex i j = i*cols+j
            
            let mutable maxrowheight = Array.create rows (0.f, 0.f)
            let mutable maxcolwidth = Array.create cols (0.f, 0.f)

            
            Array.iter(fun x -> eval1(g, x, level + 1))elements
            container.Width <- 0.f
            container.Height <- 0.f
            //Con array.iteri dava errore in quanto usavo variabili mutable (maxrowheight e maxcolwidth) in una sub-function
            node.ExprString <- "matrix(" + rows.ToString() + "," + cols.ToString()

            for i=0 to elements.Length - 1 do
                        let current = elements.[i]
                        node.ExprString <- node.ExprString + "," + current.ExprString 
                                    
                        let row, col = i / cols, i % cols
                      
                        if col=0 then 
                            let curmaxrowheight = Array.maxBy(fun (x:Expr) -> x.Container.Height) (Array.sub elements i cols)
                            maxrowheight.[row] <- match row with
                                                  | 0 -> 0.f, curmaxrowheight.Container.Height 
                                                  | _ -> fst maxrowheight.[row-1] + snd maxrowheight.[row-1], curmaxrowheight.Container.Height
                            container.Height <- container.Height + curmaxrowheight.Container.Height 

                        if row=0 then 
                            let curmaxcolwidth = Array.maxBy(fun (x:Expr) -> x.Container.Width) (Array.init rows (fun index -> elements.[getindex index col]))
                            maxcolwidth.[col] <- match col with
                                                 | 0 -> 0.f, curmaxcolwidth.Container.Width 
                                                 | _ -> fst maxcolwidth.[col-1] + snd maxcolwidth.[col-1], curmaxcolwidth.Container.Width
                            container.Width <- container.Width + curmaxcolwidth.Container.Width 

                        let mutable curbox = current.Container      
                        curbox.X <- fst maxcolwidth.[col] + snd maxcolwidth.[col] / 2.f - curbox.Width / 2.f         
                        curbox.Y <- fst maxrowheight.[row] + snd maxrowheight.[row] / 2.f - curbox.Height / 2.f
                        current.Container <- curbox
                        
            node.MiddleY <- container.Height / 2.f
            node.ExprString <- node.ExprString + ")"
            ()
        |Sqrt(exp, rad) ->
            eval1(g, exp, level+1)
            eval1(g, rad, level)

            
            let mutable sqrtbox = new RectangleF(0.f, 0.f, 0.f, 0.f) 
            let mutable expbox = exp.Container 
            let mutable radbox = rad.Container 
            node.ExprString <- "sqrt(" + exp.ExprString + "," + rad.ExprString + ")"

            sqrtbox.Height <- System.Math.Max(32.f, radbox.Height + 2.f)
            sqrtbox.Width <- getSqrtBoxWidth sqrtbox.Height

            let controlpoint = getSqrtControlPoint sqrtbox
            //33 25

            expbox.X <- 0.f
            expbox.Y <- 0.f
            printf "exbox.Location: (%f, %f); exbox.Size: (%f, %f); controlpoint: (%f, %f)" expbox.X expbox.Y expbox.Width expbox.Height controlpoint.X controlpoint.Y 
            sqrtbox.X <- System.Math.Max(0.f, expbox.Width - controlpoint.X)
            if expbox.Height > controlpoint.Y then
                sqrtbox.Y <- System.Math.Max(3.f, expbox.Height - controlpoint.Y)
            else
                sqrtbox.Y <- 0.f
                expbox.Y <- controlpoint.Y - expbox.Height - 2.f

            radbox.X <- sqrtbox.X + sqrtbox.Width 
            radbox.Y <- sqrtbox.Y + sqrtbox.Height - radbox.Height//sqrtbox.Y + 2.f
            
            container.Width <- sqrtbox.X + sqrtbox.Width + radbox.Width 
            container.Height <- sqrtbox.Y + sqrtbox.Height + 1.f

            node.MiddleY <- radbox.Y + rad.MiddleY //container.Height / 2.f
            printf "nodebox: %s\nexpbox: %s\nradbox: %s\n" (sqrtbox.ToString()) (expbox.ToString()) (radbox.ToString())

            

            exp.Container <- expbox
            rad.Container <- radbox
            node.Box <- sqrtbox

        |Limit(p1, arg) ->
            
            eval1(g, p1, level+1)
            eval1(g, arg, level)
            let mutable p1box = p1.Container 
            let mutable argbox = arg.Container 
            let mutable limbox = measureString(g, "lim", font(level, false))
            let leftheight = p1box.Height + limbox.Height 
            let maxwidth = System.Math.Max(limbox.Width, p1box.Width)

            let localmiddleY = limbox.Height / 2.f
            let maxmiddleY = System.Math.Max(localmiddleY, arg.MiddleY)
            node.ExprString <- "limit(" + p1.ExprString + "," + arg.ExprString + ")"
            limbox.X <- (maxwidth - limbox.Width) / 2.f
            limbox.Y <- maxmiddleY - localmiddleY //if (leftheight>argbox.Height) then 0.f else (argbox.Height - limbox.Height) / 2.f
            argbox.Location <- PointF(maxwidth + 2.f, maxmiddleY - arg.MiddleY) //(maxsize.Height - argbox.Height) / 2.f))
            p1box.Location <- PointF((maxwidth - p1box.Width) / 2.f, limbox.Y + limbox.Height) 
            container.Size <- SizeF(argbox.X + argbox.Width, System.Math.Max(p1box.Y + p1box.Height, argbox.Y + argbox.Height))
             
            node.MiddleY <- limbox.Y + limbox.Height / 2.f
            node.Box <- limbox
            p1.Container <- p1box
            arg.Container <- argbox
  

        |Literal(str) ->
            let litbox = measureString(g, str, font(level, false))
            node.MiddleY <- litbox.Height / 2.f
            node.Box <- litbox
            container <- litbox
            node.ExprString <- str
        | _ ->  ()
        node.Container <- container
    

